package Concorrentes;

public class Carro implements Runnable {

	private int id;
	private int trechoAtual;
	private int velocidade;
	private double posicao;
	private boolean noAcostamento;
	private boolean veloz;
	private final int cor = (int)(Math.random()*9.0 + 1.0);
	private int direcao; // 0 - Praia-São Paulo, 1 - São Paulo-Praia
	private int memoriaVelocidade; 
	
	private final double velocidadeMaxima = 40.0;
	private final double velocidadeMinima = 10.0;
	
	Carro(int id){
		this.id = id;
		direcao = (int)(Math.random() + 0.5);
		if(direcao == 0)
			setTrechoAtual(0);
		else
			setTrechoAtual(Controlador.getNumeroTrechos() - 1);
		setNoAcostamento(false);
		if(direcao == 0)
			setPosicao(0.0);
		else
			setPosicao(10.0);
		veloz = (Math.random() < 0.20); // Carro é veloz com uma certa probabilidade p
		memoriaVelocidade = -1;
	}
	
	public synchronized int getDirecao(){
		return direcao;
	}
	
	public synchronized boolean isVeloz(){
		return veloz;
	}
	
	public synchronized void setVeloz(boolean v){
		veloz = v;
	}
	
	public int getCor(){
		return cor;
	}
	
	public void run() {
		
		while(!chegouNoDestino()){
			
			// Faz alguma coisa aleatoria
			fazAlgumaCoisa();
			
			//System.out.println("Carro " + getId() + " entrando no trecho " + getTrechoAtual());
			entraNoTrecho(getTrechoAtual());
			
			// Evitar ser acordado espontaneamente antes da hora
			while(!terminouTrecho()){
				esperaSerAcordado();
			}
			proximoTrecho();
		}
	}
	
	private boolean terminouTrecho(){
		if(direcao == 0)
			return(getPosicao() > Controlador.getTamanhoTrecho());
		else
			return(getPosicao() < 0.0);
	}
	
	public synchronized boolean chegouNoDestino(){
		if(getTrechoAtual() >= Controlador.getNumeroTrechos() || getTrechoAtual() < 0){
			if(Controlador.fimDaSimulacao()){
				return true;
			} else {
				if(direcao == 0)
					voltaParaPraia();
				else
					voltaParaSaoPaulo();
			}
		}
		return false;
	}

	private synchronized void voltaParaSaoPaulo() {
		setPosicao(10.0);
		setTrechoAtual(Controlador.getNumeroTrechos() - 1);
	}

	private synchronized void voltaParaPraia() {
		setPosicao(0.0);
		setTrechoAtual(0);
	}

	private void fazAlgumaCoisa() {
		try {
			Thread.sleep((int)(Math.random()*5000.0));
		} catch (InterruptedException e) {
		}
	}

	private synchronized void esperaSerAcordado() {
		try {
			wait();
		} catch (InterruptedException e) {}
	}

	public synchronized int getTrechoAtual() {
		return trechoAtual;
	}

	private synchronized void proximoTrecho() {
		if(getDirecao() == 0){
			// Esqueceu a carteira em casa, pega o retorno
			if(Math.random() > 0.95){
				setPosicao(10.0);
				setDirecao(1 - getDirecao());
			} else {
				setTrechoAtual(trechoAtual + 1);
				setPosicao(0.0);
			}
		} else {
			if(Math.random() > 0.95){
				setPosicao(0.0);
				setDirecao(1 - getDirecao());
			} else {
				setTrechoAtual(trechoAtual - 1);
				setPosicao(10.0);
			}
		}
	}

	private void setDirecao(int novaDirecao) {
		direcao = novaDirecao;
	}

	private synchronized void entraNoTrecho(int trecho) {
		setVelocidade(geraVelocidade());
		if(memoriaVelocidade == -1) memoriaVelocidade = velocidade;
		Controlador.carroQuerEntrar(this, trecho, direcao);
	}

	// Velocidade do carro, entre velocidadeMinima e velocidadeMaxima
	private synchronized int geraVelocidade() {
		return (int)(Math.random()*(velocidadeMaxima - velocidadeMinima) + velocidadeMinima);
	}

	public synchronized void setPosicao(double posicao) {
		this.posicao = posicao;
	}

	public synchronized double getPosicao() {
		return posicao;
	}

	public int getId() {
		return id;
	}
	
	public synchronized void setVelocidade(int velocidade){
		this.memoriaVelocidade = this.velocidade;
		this.velocidade = velocidade;
	}
	
	public synchronized int getVelocidade(){
		return velocidade;
	}
	
	public synchronized void setTrechoAtual(int trechoAtual){
		this.trechoAtual = trechoAtual;
	}

	public synchronized void acorda() {
		notify();
	}

	public synchronized void setNoAcostamento(boolean noAcostamento) {
		this.noAcostamento = noAcostamento;
	}

	public synchronized boolean isNoAcostamento() {
		return noAcostamento;
	}

	public synchronized void saiDoAcostamento() {
		// Muda conduta com probabilidade 1/2
		boolean mudaConduta = (Math.random() > 0.5);
		if(mudaConduta){
			setVeloz(!isVeloz());
		}
		setNoAcostamento(false);
	}
	
	public void esperaPedestrePassar() {
		setVelocidade(0);
	}
	
	public int getMemoriaVelocidade(){
		return memoriaVelocidade;
	}
}
